package com.bfl.learning.spring.context;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ClasspathXmlApplicationContext {

    /** 存储单例对象容器 */
    private Map<String, Object> singletonBeanFactory;
    /** 存储创建类定义对象的容器 */
    private Map<String, Class<?>> beanDefinationFactory;
    /** 存储beanElement对象容器 */
    private Map<String, Element> beanEleMap;
    /** 存储bean的scope属性容器 */
    private Map<String, String> beanScopeMap;

    public ClasspathXmlApplicationContext(String xmlPath) {
        singletonBeanFactory = new ConcurrentHashMap<>();
        beanDefinationFactory = new ConcurrentHashMap<>();
        beanEleMap = new ConcurrentHashMap<>();
        beanScopeMap = new ConcurrentHashMap<>();
        this.init(xmlPath);
    }

    @SuppressWarnings("unchecked")
    private void init(String xmlPath) {

        SAXReader reader = new SAXReader();
        try {
            InputStream is = getClass().getClassLoader().getResourceAsStream(xmlPath);
            Document doc = reader.read(is);
            Element root = doc.getRootElement();
            List<Element> beanElements = root.elements("bean");
            for (Element beanEle : beanElements) {
                String beanId = beanEle.attributeValue("id");
                beanEleMap.put(beanId, beanEle);

                String beanScope = beanEle.attributeValue("scope");
                if (beanScope != null) {
                    beanScopeMap.put(beanId, beanScope);
                }

                Class<?> beanClass = Class.forName(beanEle.attributeValue("class"));
                if (beanClass != null) {
                    beanDefinationFactory.put(beanId, beanClass);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Object getBean(String beanId) {
        Class<?> beanClass = beanDefinationFactory.get(beanId);
        Element beanEle = beanEleMap.get(beanId);
        String beanScope = beanScopeMap.get(beanId);

        Object bean = null;
        try {
            if ("singleton".equals(beanScope) || null == beanScope) {
                if (singletonBeanFactory.get(beanId) == null) {
                    Object instance = beanClass.newInstance();
                    singletonBeanFactory.put(beanId, instance);
                }
                bean = singletonBeanFactory.get(beanId);
            }
            if ("prototype".equals(beanScope)) {
                bean = beanClass.newInstance();
            }
            this.setFieldValues(beanId, beanEle, beanScope, beanClass, bean);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return bean;
    }

    @SuppressWarnings("unchecked")
    public <T>T getBean(String beanId, Class<T> clazz) {
        return (T) this.getBean(beanId);
    }

    @SuppressWarnings("unchecked")
    private void setFieldValues(String beanId, Element beanEle, String beanScope, Class<?> beanClass, Object bean) {

        try {
            List<Element> propEles = beanEle.elements("property");
            if (propEles == null) {
                return;
            }

            for (Element propEle : propEles) {
                String fieldName = propEle.attributeValue("name");
                String fieldValue = propEle.attributeValue("value");

                Field field = beanClass.getDeclaredField(fieldName);
                field.setAccessible(true);

                String fieldTypeName = field.getType().getName();
                if ("int".equals(fieldTypeName) || "java.lang.Integer".equals(fieldTypeName)) {
                    int intFieldValue = Integer.parseInt(fieldValue);
                    field.set(bean, intFieldValue);
                }
                if ("java.lang.String".equals(fieldTypeName)) {
                    field.set(bean, fieldValue);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void destroy() {
        singletonBeanFactory.clear();
        singletonBeanFactory = null;

        beanDefinationFactory.clear();
        beanDefinationFactory = null;

        beanEleMap.clear();
        beanEleMap = null;

        beanScopeMap.clear();
        beanScopeMap = null;
    }

}
